<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>

  <link rel="stylesheet" href="qunit.css" type="text/css" media="screen"/>
  <script type="text/javascript" src="qunit.js"></script>
  <script type="text/javascript" src="jquery.js"></script>

  <script type="text/javascript" src="../build/dat.gui.js"></script>
  <script type="text/javascript">

  $.noConflict();
  jQuery(document).ready(function($) {
    var math = dat.color.math;
    var interpret = dat.color.interpret;
    var Color = dat.color.Color;
    var dom = dat.dom.dom;
    var Controller = dat.controllers.Controller;
    var BooleanController = dat.controllers.BooleanController;
    var OptionController = dat.controllers.OptionController;
    var StringController = dat.controllers.StringController;
    var NumberController = dat.controllers.NumberController;
    var NumberControllerBox = dat.controllers.NumberControllerBox;
    var NumberControllerSlider = dat.controllers.NumberControllerSlider;
    var FunctionController = dat.controllers.FunctionController;
    var ColorController = dat.controllers.ColorController;
    var GUI = dat.gui.GUI;


    module("Color Math");

    test("rgb_to_hex", function() {

      equal(
          math.rgb_to_hex(100, 255, 20),
          0x64ff14
      );

    });

    test("component_from_hex", function() {

      equal(
          math.component_from_hex(0xff3366, 0),
          0x66,
          'get blue'
      );

      equal(
          math.component_from_hex(0xff3366, 1),
          0x33,
          'get green'
      );

      equal(
          math.component_from_hex(0xff3366, 2),
          0xff,
          'get red'
      );

    });

    test("hex_with_component", function() {

      equal(
          math.hex_with_component(0x002203, 0, 0x32),
          0x002232,
          'replace blue'
      );

      equal(
          math.hex_with_component(0x002203, 1, 0x32),
          0x003203,
          'replace green'
      );

      equal(
          math.hex_with_component(0x002203, 2, 0x32),
          0x322203,
          'replace red'
      );

    });

    test("rgb_to_hsv", function() {

      match(
          math.rgb_to_hsv(173, 52, 141),
          {
            h: 315.86776859504135,
            s: 0.6994219653179191,
            v: 0.6784313725490196
          });

      match(
          math.rgb_to_hsv(10, 10, 10),
          {
            h: NaN,
            s: 0,
            v: 0.0392156862745098
          }, 'grayscale');

      match(
          math.rgb_to_hsv(0, 0, 0),
          {
            h: NaN,
            s: 0,
            v: 0
          }, 'black');

    });

    test("hsv_to_rgb", function() {

      match(
          math.hsv_to_rgb(255, 0.85, 0.46),
          {
            r: 42.52125000000001,
            g: 17.595000000000006,
            b: 117.30000000000001
          });

      match(
          math.hsv_to_rgb(0, 0, 0.46),
          {
            r: 117.30000000000001,
            g: 117.30000000000001,
            b: 117.30000000000001
          }, 'grayscale');

    });

    module("Color Interpretations");

    test("CSS Strings", function() {

      match(
          interpret('#ccc'),
          {
            hex: 0xcccccc,
            space: 'HEX',
            conversionName: 'THREE_CHAR_HEX'
          },
          'THREE_CHAR_HEX'
      );

      match(
          interpret('#f09'),
          {
            hex: 0xff0099,
            space: 'HEX',
            conversionName: 'THREE_CHAR_HEX'
          },
          'THREE_CHAR_HEX'
      );

      match(
          interpret('#0f93cd'),
          {
            hex: 0x0f93cd,
            space: 'HEX',
            conversionName: 'SIX_CHAR_HEX'
          },
          'SIX_CHAR_HEX'
      );

      match(
          interpret('rgba(255,10,200,0.3)'),
          {
            r: 255,
            g: 10,
            b: 200,
            a: 0.3,
            space: 'RGB',
            conversionName: 'CSS_RGBA'
          },
          'CSS_RGBA'
      );

      match(
          interpret('rgb(255,10,200)'),
          {
            r: 255,
            g: 10,
            b: 200,
            space: 'RGB',
            conversionName: 'CSS_RGB'
          },
          'CSS_RGB'
      );

    });

    test("Other", function() {

      match(
          interpret(0xff3322),
          {
            hex: 0xff3322,
            space: 'HEX',
            conversionName: 'HEX'
          },
          'HEX'
      );

      match(
          interpret([255, 255, 0]),
          {
            r: 255,
            g: 255,
            b: 0,
            space: 'RGB',
            conversionName: 'RGB_ARRAY'
          },
          'RGB_ARRAY'
      );


      match(
          interpret([0, 110, 255, 0.3]),
          {
            r: 0,
            g: 110,
            b: 255,
            a: 0.3,
            space: 'RGB',
            conversionName: 'RGBA_ARRAY'
          },
          'RGBA_ARRAY'
      );

      match(
          interpret({
                r: 255,
                g: 255,
                b: 200
              }),
          {
            r: 255,
            g: 255,
            b: 200,
            space: 'RGB',
            conversionName: 'RGB_OBJ'
          },
          'RGB_OBJ'
      );

      match(
          interpret({
                r: 255,
                g: 255,
                b: 200,
                a: 0.2
              }),
          {
            r: 255,
            g: 255,
            b: 200,
            a: 0.2,
            space: 'RGB',
            conversionName: 'RGBA_OBJ'
          },
          'RGBA_OBJ'
      );

      match(
          interpret({
                h: 360,
                s: 1,
                v: 0.5
              }),
          {
            h: 360,
            s: 1,
            v: 0.5,
            space: 'HSV',
            conversionName: 'HSV_OBJ'
          },
          'HSV_OBJ'
      );

      match(
          interpret({
                h: 360,
                s: 1,
                v: 0.5,
                a: 0.8
              }),
          {
            h: 360,
            s: 1,
            v: 0.5,
            a: 0.8,
            space: 'HSV',
            conversionName: 'HSVA_OBJ'
          },
          'HSVA_OBJ'
      );

      match(
          interpret('Failuuureeee'),
          false,
          'FAIL'
      );

    });

    module("Color Objects");

    test("Creation", function() {

      var c = new Color(255, 100, 20, 0.3);

      equal(c.r, 255, 'red');
      equal(c.g, 100, 'green');
      equal(c.b, 20, 'blue');
      equal(c.a, 0.3, 'alpha');

      equal(c.hex, 0xff6414, 'hex');

      equal(Math.round(c.h), 20, 'hue');
      equal(Math.round(c.s * 100), 92, 'saturation');
      equal(Math.round(c.v * 100), 100, 'value');

    });

    test("RGB Modification", function() {

      var c = new Color(255, 100, 20, 0.3);

      c.r -= 100;

      equal(c.r, 155, 'green');
      equal(c.g, 100, 'green');
      equal(c.b, 20, 'blue');
      equal(c.a, 0.3, 'alpha');

      equal(c.hex, 0x9b6414, 'hex');

      equal(Math.round(c.h), 36, 'hue');
      equal(Math.round(c.s * 100), 87, 'saturation');
      equal(Math.round(c.v * 100), 61, 'value');

    });

    test("RGB Modification", function() {

      var c = new Color(255, 100, 20, 0.3);

      c.r -= 100;

      equal(c.r, 155, 'green');
      equal(c.g, 100, 'green');
      equal(c.b, 20, 'blue');
      equal(c.a, 0.3, 'alpha');

      equal(c.hex, 0x9b6414, 'hex');

      equal(Math.round(c.h), 36, 'hue');
      equal(Math.round(c.s * 100), 87, 'saturation');
      equal(Math.round(c.v * 100), 61, 'value');

    });

    test("Setting RGB, Modifying HSV", function() {

      var c = new Color(255, 0, 100);

      c.s = 1;

      equal(c.r, 255);
      equal(c.g, 0);
      equalish(c.b, 100, 0.00001);
      equal(c.a, 1);

    });


    test("Setting HSV, Modifying RGB", function() {

      var c = new Color({ h: 340, s: 0.3, v: 0.9 });

      c.g = 0;

      equal(c.h, 312);
      equal(c.s, 1);
      equal(c.v, 0.9);
      equal(c.a, 1);

    });

    test("Grayscale Hue", function() {

      var c = new Color(120, 100, 20, 0.3);

      var prevHue = c.h;

      equal(typeof c.h, 'number');

      // Make graysale
      c.g = c.b = c.r;

      equal(c.h, prevHue, 'grayscale, hue intact');

    });

    test("Black Hue", function() {

      var c = new Color(120, 100, 20, 0.3);

      var prevHue = c.h;
      console.log('heh?');

      c.r = 0;
      c.b = 0;
      c.g = 0;

      equal(c.h, prevHue, 'black, hue intact');

    });

		function match(a, b, msg) {

			for (var i in b) {
				if (b[i] !== b[i]) {
					ok(a[i] !== a[i], msg)
				} else {
					equal(b[i], a[i], msg);
				}
			}

		}

		function equalish(a, b, tolerance, label) {
			return ok(Math.abs(a - b) < tolerance, label);
		}




    function initObject() {
      return {
        numberProperty: 10,
        stringProperty: 'foo',
        booleanProperty: false,
        functionProperty: function() {
          // do something
        },
        anotherBooleanProperty: true,
        colorProperty: "#ffffff"
      };
    }

    var hidden = document.createElement('div');
    hidden.style.display = 'none';
    document.body.appendChild(hidden);

    module("Controller");

    test("Retrieves values", function() {

      var object = initObject();

      var c1 = new Controller(object, 'numberProperty');
      var c2 = new Controller(object, 'stringProperty');
      var c3 = new Controller(object, 'booleanProperty');
      var c4 = new Controller(object, 'functionProperty');

      equal(c1.getValue(), object.numberProperty, "Number property");
      equal(c2.getValue(), object.stringProperty, "String property");
      equal(c3.getValue(), object.booleanProperty, "Boolean property");
      equal(c4.getValue(), object.functionProperty, "Function property");

    });

    test("Sets values", function() {
      var object = initObject();

      var c1 = new Controller(object, 'numberProperty');
      c1.setValue(40);

      equal(40, object.numberProperty);

    });

    module("BooleanController");

    test("Acknowledges original values", function() {

      var object = initObject();
      var c1 = new BooleanController(object, 'booleanProperty');
      var c2 = new BooleanController(object, 'anotherBooleanProperty');

      equal(c1.__checkbox.checked,
          object.booleanProperty);

console.log(c2.__checkbox.getAttribute('checked'));

      equal(c2.__checkbox.getAttribute('checked') === 'checked',
          object.anotherBooleanProperty);

    });

    test("Modifies values (starting true)", function() {

      var object = { booleanProperty: true };

      var c1 = new BooleanController(object, 'booleanProperty');

      // Must append this to body for click to work
      hidden.appendChild(c1.domElement);

      dom.fakeEvent(c1.__checkbox, 'click');
      equal(false, object.booleanProperty, 'changes');
      equal(false, c1.__checkbox.checked, 'checkbox valid');

      dom.fakeEvent(c1.__checkbox, 'click');
      equal(true, object.booleanProperty, 'changes back');

      equal(true, c1.__checkbox.checked, 'checkbox valid');



      object.booleanProperty = false;
      dom.fakeEvent(c1.__checkbox, 'click');
      equal(false, object.booleanProperty, 'maintains sync');
      equal(false, c1.__checkbox.checked, 'checkbox valid');

    });

    test("Modifies values (starting false)", function() {

      var object = { booleanProperty: false };

      var c1 = new BooleanController(object, 'booleanProperty');

      // Must append this to body for click to work
      hidden.appendChild(c1.domElement);

      dom.fakeEvent(c1.__checkbox, 'click');
      equal(true, object.booleanProperty, 'changes');
      equal(true, c1.__checkbox.checked, 'checkbox valid');

      dom.fakeEvent(c1.__checkbox, 'click');
      equal(false, object.booleanProperty, 'changes back');
      equal(false, c1.__checkbox.checked, 'checkbox valid');

      object.booleanProperty = true;
      dom.fakeEvent(c1.__checkbox, 'click');
      equal(true, object.booleanProperty, 'maintains sync');
      equal(true, c1.__checkbox.checked, 'checkbox valid');


    });

    module("OptionController");

    test("Populates with string array", function() {
      var object = initObject();
      var options = ['Jono', 'Doug', 'George'];

      var c1 = new OptionController(object, 'stringProperty',
          options);

//        hidden.appendChild(c1.domElement);

      $(c1.__select).children().each(function(index) {
        equal(options[index], this.innerHTML);
        equal(options[index], $(this).attr('value'));
      });

    });

    test("Populates with map", function() {
      var object = initObject();
      var options = {
        'Small': 0,
        'Medium': 2,
        'Large': 10
      };

      var c1 = new OptionController(object, 'stringProperty',
          options);

//        hidden.appendChild(c1.domElement);

      $(c1.__select).children().each(function(index) {
        equal(options[this.innerHTML], $(this).attr('value'));
      });

    });

    test("Acknowledges original value", function() {
      var object = initObject();
      var options = {
        'Small': 0,
        'Medium': 2,
        'Large': object.numberProperty
      };

      var c1 = new OptionController(object, 'numberProperty',
          options);

//        hidden.appendChild(c1.domElement);

      equal($(c1.__select).val(), object.numberProperty);

    });

    test("Modifies values", function() {
      var object = initObject();
      var options = {
        'Small': 0,
        'Medium': 2,
        'Large': 10
      };

      var c1 = new OptionController(object, 'numberProperty',
          options);

      var c2 = new OptionController(object, 'stringProperty', ['a', 'b', 'c']);

//        hidden.appendChild(c1.domElement);

      var elem = $(c1.__select).val(options['Medium'])[0];
      dom.fakeEvent(elem, 'change');

      equal(options['Medium'], object.numberProperty, 'Number property');

      elem = $(c2.__select).val('b')[0];
      dom.fakeEvent(elem, 'change');

      equal('b', object.stringProperty, 'String property');

    });

    module("StringController");

    test("Acknowledges original value", function() {
      var object = initObject();
      var c1 = new StringController(object, 'stringProperty');

      equal($(c1.__input).val(), object.stringProperty);

    });

    test("Modifies values", function() {
      var object = initObject();
      var c1 = new StringController(object, 'stringProperty');

      var newVal = (new Date()).toJSON();

      var elem = $(c1.__input).val(newVal)[0];
      dom.fakeEvent(elem, 'change');

      equal(newVal, object.stringProperty);

    });

    module("NumberController");

    test("Constraints", function() {
      var object = initObject();
      var params = { min: 0, max: 10, step: 2 };

      var c1 = new NumberController(object,
          'numberProperty', params);

      c1.setValue(12);
      equal(object.numberProperty, params.max, "Maximum values");

      c1.setValue(-20);
      equal(object.numberProperty, params.min, "Minimum values");

      c1.setValue(1);
      equal(object.numberProperty, 2, "Steps");

    });

    module("NumberControllerBox");

    test("Acknowledges original value", function() {
      var object = initObject();
      var c1 = new NumberControllerBox(object,
          'numberProperty');

//        var newVal = Date.now();
//
//        $(c1.__input).val(newVal).trigger('change');
//
      equal($(c1.__input).val(), object.numberProperty.toString());

    });

    test("Modifies value", function() {
      var object = initObject();
      var c1 = new NumberControllerBox(object,
          'numberProperty');

      var newVal = Date.now();

      var elem = $(c1.__input).val(newVal)[0];
      dom.fakeEvent(elem, 'change');

      equal(typeof object.numberProperty, 'number');
      equal(object.numberProperty, newVal);

    });


    test("Handles invalid input", function() {
      var object = initObject();
      var c1 = new NumberControllerBox(object,
          'numberProperty');

      var newVal = '~! I98* omg this is not a N&^&*^umber.e-083.9';
      var prevVal = object.numberProperty;

      var elem = $(c1.__input).val(newVal)[0];
      dom.fakeEvent(elem, 'change');

      equal(typeof object.numberProperty, 'number');
      equal(object.numberProperty, prevVal);

    });


    test("Handles drag", function() {

      var object = { numberProperty: 0 };
      var params = { step: Math.random() * 21 };

      var c1 = new NumberControllerBox(object,
          'numberProperty', params);

      var prevVal = object.numberProperty;
      var disp = Math.round(Math.random() * 100);

      var elem = c1.__input;

      dom.fakeEvent(elem, 'mousedown', {
        x: 0,
        y: 0
      });
      dom.fakeEvent(window, 'mousemove', {
        x: 0,
        y: disp
      });
      dom.fakeEvent(window, 'mouseup');

      equal(object.numberProperty, prevVal + params.step * -disp);

    });

    module("NumberControllerSlider");

    test("Acknowledges original value", function() {

      var object = initObject();

      var min = 0, max = 50;

      var c1 = new NumberControllerSlider(object, 'numberProperty', min, max);

      document.body.appendChild(c1.domElement);

      var bw = dom.getWidth(c1.__background);
      var fw = dom.getWidth(c1.__foreground);

      document.body.removeChild(c1.domElement);

      equalish(fw/bw, (object.numberProperty - min) / (max - min), 0.01, 'Slider width indicative of value.');

    });

    test("Modifies values", function() {

      var object = initObject();

      var min = 0, max = 50;

      var c1 = new NumberControllerSlider(object, 'numberProperty', min, max, 1);

      document.body.appendChild(c1.domElement);

      var o = dom.getOffset(c1.domElement);
      var w = dom.getWidth(c1.domElement);

      dom.fakeEvent(c1.__background, 'mousedown', {
        x: o.left + w/2,
        y: o.top
      });

      var bw = dom.getWidth(c1.__background);
      var fw = dom.getWidth(c1.__foreground);

      equal(object.numberProperty, (min+max)/2, 'Mouse down');

      equalish(fw/bw, (object.numberProperty - min) / (max - min), 0.01, 'Slider width still indicative of value.');

      dom.fakeEvent(window, 'mousemove', {
        x: o.left,
        y: o.top
      });

      fw = dom.getWidth(c1.__foreground);


      equal(object.numberProperty, min, 'Mouse move');

      equal(fw/bw, (object.numberProperty - min) / (max - min), 'Slider width still indicative of value.');


      dom.fakeEvent(window, 'mouseup');

      dom.fakeEvent(window, 'mousemove', {
        x: o.left+w,
        y: o.top
      });

      equal(object.numberProperty, min, 'Mouse releases drag');

      document.body.removeChild(c1.domElement);


    });

    module("ColorController");

    test("Get Color", function() {
      var object = initObject();
      var c1 = new ColorController(object, 'colorProperty');
      document.body.appendChild(c1.domElement);

      var input = c1.domElement.getElementsByTagName("input")[0];
      equal(input.value, object.colorProperty, "Input value is the same as the colorProperty");

      document.body.removeChild(c1.domElement);
    });

/*
    test("Set Color", function() {
      // get from click, get from hover
      var object = initObject();
      var c1 = new ColorController(object, 'colorProperty');

      document.body.appendChild(c1.domElement);
      var input = c1.domElement.getElementsByTagName("input")[0];

      // type in color
      input.value = "#ff0";

      // TODO fake events for keys not working
      dom.fakeEvent(input, 'keydown', { keyCode: 13 });

      // click sv field

      // click hue slider

      equal(1,0, "TODO: add set color tests.");

      document.body.removeChild(c1.domElement);
    });
*/
    module("Controller Events");

    test("onChange", function() {

      var object = initObject();

      var c0 = new NumberControllerSlider(object, 'numberProperty');
      var c1 = new NumberControllerBox(object, 'numberProperty');
      var c2 = new StringController(object, 'stringProperty');
      var c3 = new BooleanController(object, 'booleanProperty');
      var c4 = new FunctionController(object, 'functionProperty');
      var c5 = new OptionController(object, 'stringProperty', [0,1,2]);

      var c0_changed = false;
      var c1_changed = false;
      var c2_changed = false;
      var c3_changed = false;
      var c4_changed = false;
      var c5_changed = false;

      c0.onChange(function() {
        c0_changed = true;
      });

      c1.onChange(function() {
        c1_changed = true;
      });

      c2.onChange(function() {
        c2_changed = true;
      });

      c3.onChange(function() {
        c3_changed = true;
      });

      c4.onChange(function() {
        c4_changed = true;
      });

      c5.onChange(function() {
        c5_changed = true;
      });

      hidden.appendChild(c3.domElement);

      c0.setValue(0.5);
      c1.setValue(10);
      c2.setValue('hey');
      c3.setValue(false);
      c4.fire();
      c5.setValue('yo');

      ok(c1_changed, 'NumberControllerSlider');
      ok(c1_changed, 'NumberControllerBox');
      ok(c2_changed, 'StringController');
      ok(c3_changed, 'BooleanController');
      ok(c4_changed, 'FunctionController');
      ok(c5_changed, 'OptionController');

    });

    test("onFinishChange", function() {

      var object = initObject();

      var min = 0, max = 100;

      var c0 = new NumberControllerSlider(object, 'numberProperty', min, max);
      var c1 = new NumberControllerBox(object, 'numberProperty');
      var c2 = new StringController(object, 'stringProperty');

      var c0_changed = false;
      var c1_changed = false;
      var c2_changed = false;

      c0.onFinishChange(function() {
        c0_changed = true;
      });

      document.body.appendChild(c0.domElement);

      var o = dom.getOffset(c0.domElement);
      var w = dom.getWidth(c0.domElement);

      dom.fakeEvent(c0.__background, 'mousedown', {
        x: o.left + w/2,
        y: o.top
      });

      ok(!c0_changed, 'NumberControllerSlider didn\'t jump the gun ...');

      dom.fakeEvent(window, 'mousemove', {
        x: o.left,
        y: o.top
      });
      dom.fakeEvent(window, 'mouseup', {
        x: o.left,
        y: o.top
      });

      ok(c0_changed, 'NumberControllerSlider fires when needed.');

      document.body.removeChild(c0.domElement);

      c1.onFinishChange(function() {
        c1_changed = true;
      });

      document.body.appendChild(c1.domElement);

      c1.__input.focus();
      c1.__input.value = '1';
      ok(!c1_changed, 'NumberControllerBox didn\'t jump the gun ...');
      c1.__input.value += '2';
      c1.__input.blur();

      document.body.removeChild(c1.domElement);

      ok(c1_changed, 'NumberControllerBox fires when needed.');

      c2.onFinishChange(function() {
        c2_changed = true;
      });

      document.body.appendChild(c2.domElement);

      c2.__input.focus();
      c2.__input.value = 'friendBudd';
      ok(!c2_changed, 'StringController didn\'t jump the gun ...');
      c2.__input.value += 'y';
      c2.__input.blur();

      document.body.removeChild(c2.domElement);

      ok(c2_changed, 'StringController fires when needed.');

    });

		function equalish(a, b, tolerance, label) {
			return ok(Math.abs(a - b) < tolerance, label);
		}



    module('GUI Appearance');

    test('Auto placement', function() {

      var gui = new GUI();
      gui.add({ x: 0 }, 'x');

      var gui2 = new GUI();
      gui2.add({ x: 0 }, 'x');

      equal($('.dg.ac').length, 1, 'A single auto-place container created');
      equal($('.dg.ac').children().length, 2, 'Containing two GUI\'s');

      equal(gui.parent, undefined);
      equal(gui2.parent, undefined);

      $('.dg.ac').children().each(function(key, value) {
        ok($(value).hasClass(GUI.CLASS_AUTO_PLACE), 'GUI has auto-place class');
        ok($(value).hasClass(GUI.CLASS_MAIN), 'GUI has main class');

      });

      gui.destroy();
      gui2.destroy();

    });

    test('Auto placement scroll', function() {

      var gui = new GUI();

      // Add a lot of controllers. This will fail if you have some freakishly tall monitor.
      for (var i = 0; i < 100; i++) {
        gui.add({ x: 0 }, 'x');
      }


      setTimeout(function() {
        ok($(gui.domElement).hasClass(GUI.CLASS_TOO_TALL), 'GUI has too tall class');
        notEqual($(gui.domElement).children('ul')[0].style.height, 'auto');

        gui.destroy();
      }, 0);
    });

    test('close/open button position', function() {

      var gui = new GUI({closeOnTop:true});
      var gui2 = new GUI({closeOnTop:false});
      var gui3 = new GUI();

      ok($(gui.domElement).find('ul').prev().hasClass(GUI.CLASS_CLOSE_BUTTON) && $(gui.domElement).find('ul').prev().hasClass(GUI.CLASS_CLOSE_TOP), 'GUI has close/open button on top');
      ok($(gui2.domElement).find('ul').next().hasClass(GUI.CLASS_CLOSE_BUTTON) && $(gui2.domElement).find('ul').next().hasClass(GUI.CLASS_CLOSE_BOTTOM), 'GUI has close/open button on bottom');
      ok($(gui3.domElement).find('ul').next().hasClass(GUI.CLASS_CLOSE_BUTTON) && $(gui3.domElement).find('ul').next().hasClass(GUI.CLASS_CLOSE_BOTTOM), 'GUI has close/open button on bottom by default');

      gui.destroy();
      gui2.destroy();
      gui3.destroy();

    });

    test('Folders', function() {

      var gui = new GUI();
      gui.add({x:0}, 'x');

      var name1 = 'name';
      var f1 = gui.addFolder(name1);

      f1.add({ x: 0 }, 'x');

      equal(f1.name, name1, "Accepts name");
      equal($(f1.domElement).find('li.title').html(), name1, "Displays name");

      equal(f1.closed, true, "Closed by default");
      ok($(f1.domElement).find('ul').hasClass(GUI.CLASS_CLOSED), "Has closed class");

      var title = $(f1.domElement).find('li.title')[0];

      dom.fakeEvent(title, 'click');

      equal(f1.closed, false, "Opens on click");
      ok(!$(f1.domElement).find('ul').hasClass(GUI.CLASS_CLOSED), "Opens on click");

      dom.fakeEvent(title, 'click');

      equal(f1.closed, true, "Closes back up");
      ok($(f1.domElement).find('ul').hasClass(GUI.CLASS_CLOSED), "Closes back up");

      gui.destroy();

    });


    module("GUI Controller Methods");

    test('options', function() {

      var gui = new GUI();

      var controller = gui.add({ x: 0 }, 'x').options(0, 1, 2, 3, 4);

      $(controller.__select).children().each(function(key, value) {
        equals(value.innerHTML, key, 'By array name okay');
        equals(value.value, key, 'By array value okay');
      });

      controller = gui.add({ x: 0 }, 'x').options(
          {
            0: '0',
            1: '1',
            2: '2',
            3: '3',
            4: '4'
          }
      );

      $(controller.__select).children().each(function(key, value) {
        equals(value.innerHTML, key, 'By array name okay');
        equals(value.value, key, 'By array value okay');
      });

      gui.destroy();

    });

    test('name', function() {

      var gui = new GUI();

      var name = 'hey man';
      var name2 = 'yoyoo';

      var controller = gui.add({ x: 0 }, 'x').name(name);

      equals($(controller.__li).find('.property-name').html(), name);

      controller.name(name2);
      equals($(controller.__li).find('.property-name').html(), name2);

      gui.destroy();

    });

    test('listen', function() {

      var gui = new GUI();

      var obj = { x: 0 };

      var returned1 = gui.add(obj, 'x');
      var returned2 = returned1.listen();

      obj.x = 10;

      setTimeout(function() {
        ok(returned1 === returned2, 'Returns self');

        equal(returned1.__input.value, obj.x, 'Updates display');
        gui.destroy();
      }, 0);

    });

    test('remove', function() {

      var gui = new GUI();

      var c = gui.add({x:0}, 'x');

      ok($.contains(gui.domElement, c.domElement), "Now you see it");
      c.remove();

      ok(!$.contains(gui.domElement, c.domElement), "Now you don't.");

      gui.destroy();

    });

    test('removeFolder', function() {

      var gui = new GUI();

      var f = gui.addFolder('Temporary folder');

      ok($.contains(gui.domElement, f.domElement), "Now you see it");
      gui.removeFolder(f);

      ok(!$.contains(gui.domElement, f.domElement), "Now you don't.");

      gui.destroy();

    });

    test('min, max & step', function() {

      var gui = new GUI();

      var min = -10;
      var max = 200;
      var step = 5;

      var c = gui.add({ x: 0 }, 'x');

      c.min(min);
      equals(c.__min, min, 'min');

      c.step(step);
      equals(c.__step, step, 'step');

      var c2 = c.max(max);
      equals(c.__max, max, 'max');

      notEqual(c2, c, 'Controller has changed.');

      ok($(c2.__li).find('.slider').length > 0, 'Slider added');

      equals(c.__step, step, 'step intact');

      gui.destroy();

    });

    module("GUI Controller Augmentation");

    test('Adds NumberControllerBox to sliders', function() {
      var gui = new GUI();

      var c = gui.add({ x: 0 }, 'x', 0, 10);

      ok($(c.__li).find('input').length > 0, 'NumberControllerBox added');

      gui.destroy();

    });

    test('Clickable rows for BooleanControllers', function() {

      var gui = new GUI();

      var c = gui.add({ x: false }, 'x');

      equal(c.__checkbox.checked, false, 'Acknowledges original');

      dom.fakeEvent(c.__li, 'click');

      equal($(c.__checkbox).attr('checked'), 'checked', 'Changes when I click the row');

      dom.fakeEvent(c.__li, 'click');

      equal(c.__checkbox.checked, false, 'Changes back');

      gui.destroy();

    });

    test('Clickable rows for FunctionControllers', function() {

      expect(3);

      var gui = new GUI();

      var c = gui.add({ x: function() {
        ok(true)
      } }, 'x');

      dom.fakeEvent(c.__li, 'click');
      c.fire();
      dom.fakeEvent(c.__li, 'click');

      gui.destroy();

    });

    module('GUI Saving');

    test('Remembering values', function() {

      var object = {
        number: 0,
        boolean: false,
        string: 'hey'
      };

      var controllers = {};

      var changed = {
        number: -20,
        boolean: true,
        string: 'hang'
      };

      var gui = new GUI();
      gui.remember(object);

      for (var i in object) {
        controllers[i] = gui.add(object, i);
      }

      for (i in controllers) {
        controllers[i].setValue(changed[i]);
      }

      var saveObject = gui.getSaveObject();

      gui.destroy();

      gui = new GUI({
            load: saveObject
          });

      gui.remember(object);

      for (i in object) {
        controllers[i] = gui.add(object, i);
        equal(object[i], changed[i]);
      }

      ensurePresetSelectDisplay(gui);

      gui.destroy();

    });

    test('Presets', function() {

      var presetName = 'New Preset';

      var object = {
        number: 0,
        boolean: false,
        string: 'hey'
      };

      var original = {};

      for (var i in object) {
        original[i] = object[i];
      }

      var controllers = {};

      var changed = {
        number: -20,
        boolean: true,
        string: 'hang'
      };

      var gui = new GUI();
      gui.remember(object);

      for (i in object) {
        controllers[i] = gui.add(object, i);
      }

      for (i in controllers) {
        controllers[i].setValue(changed[i]);
      }

      ensurePresetSelectDisplay(gui);

      gui.saveAs(presetName);

      ensurePresetSelectDisplay(gui);

      var saveObject = gui.getSaveObject();
      console.log(saveObject);

      gui.destroy();

      gui = new GUI({
            load: saveObject
          });

      gui.remember(object);

      for (i in object) {
        controllers[i] = gui.add(object, i);
        equal(object[i], changed[i], "Uses last defined preset");
      }

      equal(gui.preset, presetName, "Preset value correct");
      ensurePresetSelectDisplay(gui);

      gui.destroy();

      gui = new GUI({
            preset: "Default",
            load: saveObject
          });

      gui.remember(object);

      for (i in object) {
        controllers[i] = gui.add(object, i);
        equal(object[i], original[i], "Loads with explicitly set preset");
      }

      equal(gui.preset, "Default", "Preset value correct");
      ensurePresetSelectDisplay(gui);

      gui.preset = presetName;

      for (i in object) {
        equal(object[i], changed[i], "Changes via gui.preset property");
      }

      $(gui.__preset_select).val('Default');
      dom.fakeEvent(gui.__preset_select, 'change');

      for (i in object) {
        equal(object[i], original[i], "Changes via dropdown");
      }

      gui.destroy();

    });

    function ensurePresetSelectDisplay(gui) {

      equal($(gui.__preset_select).children('option:selected')[0].value, gui.preset, "Dropdown display matches preset value");

    }


});
</script>

</head>
<body>
<h1 id="qunit-header"></h1>

<h2 id="qunit-banner"></h2>

<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">test markup, will be hidden</div>
</body>
</html>
